/** @file
  Implements write firmware file.

  Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include "FwVolDriver.h"

/**
  Calculate the checksum for the FFS header.

  @param FfsHeader   FFS File Header which needs to calculate the checksum

**/
VOID
SetHeaderChecksum (
  IN EFI_FFS_FILE_HEADER *FfsHeader
  )
{
  EFI_FFS_FILE_STATE  State;
  UINT8               FileChecksum;

  //
  // The state and the File checksum are not included
  //
  State = FfsHeader->State;
  FfsHeader->State = 0;

  FileChecksum = FfsHeader->IntegrityCheck.Checksum.File;
  FfsHeader->IntegrityCheck.Checksum.File = 0;

  FfsHeader->IntegrityCheck.Checksum.Header = 0;

  if (IS_FFS_FILE2 (FfsHeader)) {
    FfsHeader->IntegrityCheck.Checksum.Header = CalculateCheckSum8 (
      (UINT8 *) FfsHeader,
      sizeof (EFI_FFS_FILE_HEADER2)
      );
  } else {
    FfsHeader->IntegrityCheck.Checksum.Header = CalculateCheckSum8 (
      (UINT8 *) FfsHeader,
      sizeof (EFI_FFS_FILE_HEADER)
      );
  }

  FfsHeader->State                          = State;
  FfsHeader->IntegrityCheck.Checksum.File   = FileChecksum;

  return ;
}

/**
  Calculate the checksum for the FFS File.

  @param FfsHeader       FFS File Header which needs to calculate the checksum
  @param ActualFileSize  The whole Ffs File Length.

**/
VOID
SetFileChecksum (
  IN EFI_FFS_FILE_HEADER *FfsHeader,
  IN UINTN               ActualFileSize
  )
{
  if ((FfsHeader->Attributes & FFS_ATTRIB_CHECKSUM) != 0) {

    FfsHeader->IntegrityCheck.Checksum.File = 0;

    if (IS_FFS_FILE2 (FfsHeader)) {
      FfsHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 (
        (UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER2),
        ActualFileSize - sizeof (EFI_FFS_FILE_HEADER2)
        );
    } else {
      FfsHeader->IntegrityCheck.Checksum.File = CalculateCheckSum8 (
        (UINT8 *) FfsHeader + sizeof (EFI_FFS_FILE_HEADER),
        ActualFileSize - sizeof (EFI_FFS_FILE_HEADER)
        );
    }

  } else {

    FfsHeader->IntegrityCheck.Checksum.File = FFS_FIXED_CHECKSUM;

  }

  return ;
}

/**
  Get the alignment value from File Attributes.

  @param FfsAttributes  FFS attribute

  @return Alignment value.

**/
UINTN
GetRequiredAlignment (
  IN EFI_FV_FILE_ATTRIBUTES FfsAttributes
  )
{
  UINTN AlignmentValue;

  AlignmentValue = FfsAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT;

  if (AlignmentValue <= 3) {
    return 0x08;
  }

  if (AlignmentValue > 16) {
    //
    // Anyway, we won't reach this code
    //
    return 0x08;
  }

  return (UINTN)1 << AlignmentValue;

}

/**
  Calculate the leading Pad file size to meet the alignment requirement.

  @param FvDevice          Cached Firmware Volume.
  @param StartAddress      The starting address to write the FFS File.
  @param BufferSize        The FFS File Buffer Size.
  @param RequiredAlignment FFS File Data alignment requirement.

  @return The required Pad File Size.

**/
UINTN
CalculatePadFileSize (
  IN FV_DEVICE            *FvDevice,
  IN EFI_PHYSICAL_ADDRESS StartAddress,
  IN UINTN                BufferSize,
  IN UINTN                RequiredAlignment
  )
{
  UINTN DataStartPos;
  UINTN RelativePos;
  UINTN PadSize;

  if (BufferSize > 0x00FFFFFF) {
    DataStartPos  = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER2);
  } else {
    DataStartPos  = (UINTN) StartAddress + sizeof (EFI_FFS_FILE_HEADER);
  }
  RelativePos   = DataStartPos - (UINTN) FvDevice->CachedFv;

  PadSize       = 0;

  while ((RelativePos & (RequiredAlignment - 1)) != 0) {
    RelativePos++;
    PadSize++;
  }
  //
  // If padsize is 0, no pad file needed;
  // If padsize is great than 24, then pad file can be created
  //
  if ((PadSize == 0) || (PadSize >= sizeof (EFI_FFS_FILE_HEADER))) {
    return PadSize;
  }

  //
  // Perhaps following method can save space
  //
  RelativePos = DataStartPos - (UINTN) FvDevice->CachedFv + sizeof (EFI_FFS_FILE_HEADER);
  PadSize     = sizeof (EFI_FFS_FILE_HEADER);

  while ((RelativePos & (RequiredAlignment - 1)) != 0) {
    RelativePos++;
    PadSize++;
  }

  return PadSize;
}

/**
  Convert EFI_FV_FILE_ATTRIBUTES to FFS_FILE_ATTRIBUTES.

  @param FvFileAttrib    The value of EFI_FV_FILE_ATTRIBUTES
  @param FfsFileAttrib   Pointer to the got FFS_FILE_ATTRIBUTES value.

**/
VOID
FvFileAttrib2FfsFileAttrib (
  IN     EFI_FV_FILE_ATTRIBUTES  FvFileAttrib,
  OUT UINT8                      *FfsFileAttrib
  )
{
  UINT8 FvFileAlignment;
  UINT8 FfsFileAlignment;
  UINT8 FfsFileAlignment2;

  FvFileAlignment   = (UINT8) (FvFileAttrib & EFI_FV_FILE_ATTRIB_ALIGNMENT);
  FfsFileAlignment  = 0;
  FfsFileAlignment2 = 0;

  switch (FvFileAlignment) {
  case 0:
    //
    // fall through
    //
  case 1:
    //
    // fall through
    //
  case 2:
    //
    // fall through
    //
  case 3:
    //
    // fall through
    //
    FfsFileAlignment = 0;
    break;

  case 4:
    //
    // fall through
    //
  case 5:
    //
    // fall through
    //
  case 6:
    //
    // fall through
    //
    FfsFileAlignment = 1;
    break;

  case 7:
    //
    // fall through
    //
  case 8:
    //
    // fall through
    //
    FfsFileAlignment = 2;
    break;

  case 9:
    FfsFileAlignment = 3;
    break;

  case 10:
    //
    // fall through
    //
  case 11:
    //
    // fall through
    //
    FfsFileAlignment = 4;
    break;

  case 12:
    //
    // fall through
    //
  case 13:
    //
    // fall through
    //
  case 14:
    //
    // fall through
    //
    FfsFileAlignment = 5;
    break;

  case 15:
    FfsFileAlignment = 6;
    break;

  case 16:
    FfsFileAlignment = 7;
    break;

  case 17:
    FfsFileAlignment = 0;
    FfsFileAlignment2 = 1;
    break;
  case 18:
    FfsFileAlignment = 1;
    FfsFileAlignment2 = 1;
    break;
  case 19:
    FfsFileAlignment = 2;
    FfsFileAlignment2 = 1;
    break;
  case 20:
    FfsFileAlignment = 3;
    FfsFileAlignment2 = 1;
    break;
  case 21:
    FfsFileAlignment = 4;
    FfsFileAlignment2 = 1;
    break;
  case 22:
    FfsFileAlignment = 5;
    FfsFileAlignment2 = 1;
    break;
  case 23:
    FfsFileAlignment = 6;
    FfsFileAlignment2 = 1;
    break;
  case 24:
    FfsFileAlignment = 7;
    FfsFileAlignment2 = 1;
    break;
  }

  *FfsFileAttrib = (UINT8) ((FfsFileAlignment << 3) | (FfsFileAlignment2 << 1));

  return ;
}

/**
  Locate a free space entry that can hold this FFS file.

  @param FvDevice          Cached Firmware Volume.
  @param Size              The FFS file size.
  @param RequiredAlignment FFS File Data alignment requirement.
  @param PadSize           Pointer to the size of leading Pad File.
  @param FreeSpaceEntry    Pointer to the Free Space Entry that meets the requirement.

  @retval EFI_SUCCESS     The free space entry is found.
  @retval EFI_NOT_FOUND   The free space entry can't be found.

**/
EFI_STATUS
FvLocateFreeSpaceEntry (
  IN  FV_DEVICE             *FvDevice,
  IN  UINTN                 Size,
  IN  UINTN                 RequiredAlignment,
  OUT UINTN                 *PadSize,
  OUT FREE_SPACE_ENTRY      **FreeSpaceEntry
  )
{
  FREE_SPACE_ENTRY  *FreeSpaceListEntry;
  LIST_ENTRY        *Link;
  UINTN             PadFileSize;

  Link                = FvDevice->FreeSpaceHeader.ForwardLink;
  FreeSpaceListEntry  = (FREE_SPACE_ENTRY *) Link;

  //
  // Loop the free space entry list to find one that can hold the
  // required the file size
  //
  while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) {
    PadFileSize = CalculatePadFileSize (
                    FvDevice,
                    (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceListEntry->StartingAddress,
                    Size,
                    RequiredAlignment
                    );
    if (FreeSpaceListEntry->Length >= Size + PadFileSize) {
      *FreeSpaceEntry = FreeSpaceListEntry;
      *PadSize        = PadFileSize;
      return EFI_SUCCESS;
    }

    FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink;
  }

  return EFI_NOT_FOUND;

}

/**
  Locate Pad File for writing, this is got from FV Cache.

  @param FvDevice           Cached Firmware Volume.
  @param Size               The required FFS file size.
  @param RequiredAlignment  FFS File Data alignment requirement.
  @param PadSize            Pointer to the size of leading Pad File.
  @param PadFileEntry       Pointer to the Pad File Entry that meets the requirement.

  @retval EFI_SUCCESS     The required pad file is found.
  @retval EFI_NOT_FOUND   The required pad file can't be found.

**/
EFI_STATUS
FvLocatePadFile (
  IN  FV_DEVICE           *FvDevice,
  IN  UINTN               Size,
  IN  UINTN               RequiredAlignment,
  OUT UINTN               *PadSize,
  OUT FFS_FILE_LIST_ENTRY **PadFileEntry
  )
{
  FFS_FILE_LIST_ENTRY *FileEntry;
  EFI_FFS_FILE_STATE  FileState;
  EFI_FFS_FILE_HEADER *FileHeader;
  UINTN               PadAreaLength;
  UINTN               PadFileSize;
  UINTN               HeaderSize;

  FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink;

  //
  // travel through the whole file list to get the pad file entry
  //
  while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) {

    FileHeader  = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader;
    FileState   = GetFileState (FvDevice->ErasePolarity, FileHeader);

    if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) {
      //
      // we find one valid pad file, check its free area length
      //
      if (IS_FFS_FILE2 (FileHeader)) {
        HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
        PadAreaLength = FFS_FILE2_SIZE (FileHeader) - HeaderSize;
      } else {
        HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
        PadAreaLength = FFS_FILE_SIZE (FileHeader) - HeaderSize;
      }

      PadFileSize = CalculatePadFileSize (
                      FvDevice,
                      (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + HeaderSize,
                      Size,
                      RequiredAlignment
                      );
      if (PadAreaLength >= (Size + PadFileSize)) {
        *PadSize      = PadFileSize;
        *PadFileEntry = FileEntry;
        return EFI_SUCCESS;
      }
    }

    FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink);
  }

  return EFI_NOT_FOUND;
}

/**
  Locate a suitable pad file for multiple file writing.

  @param FvDevice          Cached Firmware Volume.
  @param NumOfFiles        The number of Files that needed updating
  @param BufferSize        The array of each file size.
  @param RequiredAlignment The array of of FFS File Data alignment requirement.
  @param PadSize           The array of size of each leading Pad File.
  @param TotalSizeNeeded   The totalsize that can hold these files.
  @param PadFileEntry      Pointer to the Pad File Entry that meets the requirement.

  @retval EFI_SUCCESS     The required pad file is found.
  @retval EFI_NOT_FOUND   The required pad file can't be found.

**/
EFI_STATUS
FvSearchSuitablePadFile (
  IN FV_DEVICE              *FvDevice,
  IN UINTN                  NumOfFiles,
  IN UINTN                  *BufferSize,
  IN UINTN                  *RequiredAlignment,
  OUT UINTN                 *PadSize,
  OUT UINTN                 *TotalSizeNeeded,
  OUT FFS_FILE_LIST_ENTRY   **PadFileEntry
  )
{
  FFS_FILE_LIST_ENTRY *FileEntry;
  EFI_FFS_FILE_STATE  FileState;
  EFI_FFS_FILE_HEADER *FileHeader;
  UINTN               PadAreaLength;
  UINTN               TotalSize;
  UINTN               Index;
  UINTN               HeaderSize;

  FileEntry = (FFS_FILE_LIST_ENTRY *) FvDevice->FfsFileListHeader.ForwardLink;

  //
  // travel through the whole file list to get the pad file entry
  //
  while (FileEntry != (FFS_FILE_LIST_ENTRY *) &FvDevice->FfsFileListHeader) {

    FileHeader  = (EFI_FFS_FILE_HEADER *) FileEntry->FfsHeader;
    FileState   = GetFileState (FvDevice->ErasePolarity, FileHeader);

    if ((FileHeader->Type == EFI_FV_FILETYPE_FFS_PAD) && (FileState == EFI_FILE_DATA_VALID)) {
      //
      // we find one valid pad file, check its length
      //
      if (IS_FFS_FILE2 (FileHeader)) {
        HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
        PadAreaLength = FFS_FILE2_SIZE (FileHeader) - HeaderSize;
      } else {
        HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
        PadAreaLength = FFS_FILE_SIZE (FileHeader) - HeaderSize;
      }
      TotalSize     = 0;

      for (Index = 0; Index < NumOfFiles; Index++) {
        PadSize[Index] = CalculatePadFileSize (
                      FvDevice,
                      (EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader + HeaderSize + TotalSize,
                      BufferSize[Index],
                      RequiredAlignment[Index]
                      );
        TotalSize += PadSize[Index];
        TotalSize += BufferSize[Index];

        if (TotalSize > PadAreaLength) {
          break;
        }
      }

      if (PadAreaLength >= TotalSize) {
        *PadFileEntry     = FileEntry;
        *TotalSizeNeeded  = TotalSize;
        return EFI_SUCCESS;
      }
    }

    FileEntry = (FFS_FILE_LIST_ENTRY *) (FileEntry->Link.ForwardLink);
  }

  return EFI_NOT_FOUND;
}

/**
  Locate a Free Space entry which can hold these files, including
  meeting the alignment requirements.

  @param FvDevice          Cached Firmware Volume.
  @param NumOfFiles        The number of Files that needed updating
  @param BufferSize        The array of each file size.
  @param RequiredAlignment The array of of FFS File Data alignment requirement.
  @param PadSize           The array of size of each leading Pad File.
  @param TotalSizeNeeded   The got total size that can hold these files.
  @param FreeSpaceEntry    The Free Space Entry that can hold these files.

  @retval EFI_SUCCESS     The free space entry is found.
  @retval EFI_NOT_FOUND   The free space entry can't be found.

**/
EFI_STATUS
FvSearchSuitableFreeSpace (
  IN FV_DEVICE              *FvDevice,
  IN UINTN                  NumOfFiles,
  IN UINTN                  *BufferSize,
  IN UINTN                  *RequiredAlignment,
  OUT UINTN                 *PadSize,
  OUT UINTN                 *TotalSizeNeeded,
  OUT FREE_SPACE_ENTRY      **FreeSpaceEntry
  )
{
  FREE_SPACE_ENTRY  *FreeSpaceListEntry;
  LIST_ENTRY        *Link;
  UINTN             TotalSize;
  UINTN             Index;
  UINT8             *StartAddr;

  Link                = FvDevice->FreeSpaceHeader.ForwardLink;

  FreeSpaceListEntry  = (FREE_SPACE_ENTRY *) Link;

  while ((LIST_ENTRY *) FreeSpaceListEntry != &FvDevice->FreeSpaceHeader) {
    TotalSize = 0;
    StartAddr = FreeSpaceListEntry->StartingAddress;

    //
    // Calculate the totalsize we need
    //
    for (Index = 0; Index < NumOfFiles; Index++) {
      //
      // Perhaps we don't need an EFI_FFS_FILE_HEADER, the first file
      // have had its leading pad file.
      //
      PadSize[Index] = CalculatePadFileSize (
                    FvDevice,
                    (EFI_PHYSICAL_ADDRESS) (UINTN) StartAddr + TotalSize,
                    BufferSize[Index],
                    RequiredAlignment[Index]
                    );

      TotalSize += PadSize[Index];
      TotalSize += BufferSize[Index];

      if (TotalSize > FreeSpaceListEntry->Length) {
        break;
      }
    }

    if (FreeSpaceListEntry->Length >= TotalSize) {
      *FreeSpaceEntry   = FreeSpaceListEntry;
      *TotalSizeNeeded  = TotalSize;
      return EFI_SUCCESS;
    }

    FreeSpaceListEntry = (FREE_SPACE_ENTRY *) FreeSpaceListEntry->Link.ForwardLink;
  }

  return EFI_NOT_FOUND;
}

/**
  Calculate the length of the remaining space in FV.

  @param FvDevice        Cached Firmware Volume
  @param Offset          Current offset to FV base address.
  @param Lba             LBA number for the current offset.
  @param LOffset         Offset in block for the current offset.

  @return the length of remaining space.

**/
UINTN
CalculateRemainingLength (
  IN     FV_DEVICE                            *FvDevice,
  IN     UINTN                                Offset,
  OUT  EFI_LBA                                *Lba,
  OUT  UINTN                                  *LOffset
  )
{
  LIST_ENTRY      *Link;
  LBA_ENTRY       *LbaEntry;
  UINTN           Count;

  Count     = 0;
  *Lba      = 0;
  Link      = FvDevice->LbaHeader.ForwardLink;
  LbaEntry  = (LBA_ENTRY *) Link;

  while (&LbaEntry->Link != &FvDevice->LbaHeader) {
    if (Count > Offset) {
      break;
    }

    Count += LbaEntry->BlockLength;
    (*Lba)++;
    Link      = LbaEntry->Link.ForwardLink;
    LbaEntry  = (LBA_ENTRY *) Link;
  }

  if (Count <= Offset) {
    return 0;
  }

  Link      = LbaEntry->Link.BackLink;
  LbaEntry  = (LBA_ENTRY *) Link;

  (*Lba)--;
  *LOffset  = (UINTN) (LbaEntry->BlockLength - (Count - Offset));

  Count     = 0;
  while (&LbaEntry->Link != &FvDevice->LbaHeader) {

    Count += LbaEntry->BlockLength;

    Link      = LbaEntry->Link.ForwardLink;
    LbaEntry  = (LBA_ENTRY *) Link;
  }

  Count -= *LOffset;

  return Count;
}

/**
  Writes data beginning at Lba:Offset from FV. The write terminates either
  when *NumBytes of data have been written, or when the firmware end is
  reached.  *NumBytes is updated to reflect the actual number of bytes
  written.

  @param FvDevice        Cached Firmware Volume
  @param Offset          Offset in the block at which to begin write
  @param NumBytes        At input, indicates the requested write size.
                         At output, indicates the actual number of bytes written.
  @param Buffer          Buffer containing source data for the write.

  @retval EFI_SUCCESS  Data is successfully written into FV.
  @return error        Data is failed written.

**/
EFI_STATUS
FvcWrite (
  IN     FV_DEVICE                            *FvDevice,
  IN     UINTN                                Offset,
  IN OUT UINTN                                *NumBytes,
  IN     UINT8                                *Buffer
  )
{
  EFI_STATUS                          Status;
  EFI_FIRMWARE_VOLUME_BLOCK_PROTOCOL  *Fvb;
  EFI_LBA                             Lba;
  UINTN                               LOffset;
  EFI_FVB_ATTRIBUTES_2                FvbAttributes;
  UINTN                               RemainingLength;
  UINTN                               WriteLength;
  UINT8                               *TmpBuffer;

  LOffset = 0;
  RemainingLength = CalculateRemainingLength (FvDevice, Offset, &Lba, &LOffset);
  if ((UINTN) (*NumBytes) > RemainingLength) {
    *NumBytes = (UINTN) RemainingLength;
    return EFI_INVALID_PARAMETER;
  }

  Fvb = FvDevice->Fvb;

  Status = Fvb->GetAttributes (
                  Fvb,
                  &FvbAttributes
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  if ((FvbAttributes & EFI_FV2_WRITE_STATUS) == 0) {
    return EFI_ACCESS_DENIED;
  }

  RemainingLength = *NumBytes;
  WriteLength     = RemainingLength;
  TmpBuffer       = Buffer;

  do {
    Status = Fvb->Write (
                    Fvb,
                    Lba,
                    LOffset,
                    &WriteLength,
                    TmpBuffer
                    );
    if (!EFI_ERROR (Status)) {
      goto Done;
    }

    if (Status == EFI_BAD_BUFFER_SIZE) {
      Lba++;
      LOffset = 0;
      TmpBuffer += WriteLength;
      RemainingLength -= WriteLength;
      WriteLength = (UINTN) RemainingLength;

      continue;
    } else {
      return Status;
    }
  } while (1);

Done:
  return EFI_SUCCESS;
}

/**
  Create a new FFS file into Firmware Volume device.

  @param FvDevice        Cached Firmware Volume.
  @param FfsFileBuffer   A buffer that holds an FFS file,(it contains
                         a File Header which is in init state).
  @param BufferSize      The size of FfsFileBuffer.
  @param ActualFileSize  The actual file length, it may not be multiples of 8.
  @param FileName        The FFS File Name.
  @param FileType        The FFS File Type.
  @param FileAttributes  The Attributes of the FFS File to be created.

  @retval EFI_SUCCESS           FFS fle is added into FV.
  @retval EFI_INVALID_PARAMETER File type is not valid.
  @retval EFI_DEVICE_ERROR      FV doesn't set writable attribute.
  @retval EFI_NOT_FOUND         FV has no enough space for the added file.

**/
EFI_STATUS
FvCreateNewFile (
  IN FV_DEVICE                *FvDevice,
  IN UINT8                    *FfsFileBuffer,
  IN UINTN                    BufferSize,
  IN UINTN                    ActualFileSize,
  IN EFI_GUID                 *FileName,
  IN EFI_FV_FILETYPE          FileType,
  IN EFI_FV_FILE_ATTRIBUTES   FileAttributes
  )
{
  EFI_STATUS                          Status;
  EFI_FFS_FILE_HEADER                 *FileHeader;
  EFI_PHYSICAL_ADDRESS                BufferPtr;
  UINTN                               Offset;
  UINTN                               NumBytesWritten;
  UINTN                               StateOffset;
  FREE_SPACE_ENTRY                    *FreeSpaceEntry;
  UINTN                               RequiredAlignment;
  UINTN                               PadFileSize;
  FFS_FILE_LIST_ENTRY                 *PadFileEntry;
  EFI_FFS_FILE_ATTRIBUTES             TmpFileAttribute;
  FFS_FILE_LIST_ENTRY                 *FfsFileEntry;
  UINTN                               HeaderSize;

  //
  // File Type: 0x0E~0xE0 are reserved
  //
  if ((FileType > EFI_FV_FILETYPE_SMM_CORE) && (FileType < 0xE0)) {
    return EFI_INVALID_PARAMETER;
  }

  //
  // First find a free space that can hold this image.
  // Check alignment, FFS at least must be aligned at 8-byte boundary
  //
  RequiredAlignment = GetRequiredAlignment (FileAttributes);

  Status = FvLocateFreeSpaceEntry (
            FvDevice,
            BufferSize,
            RequiredAlignment,
            &PadFileSize,
            &FreeSpaceEntry
            );
  if (EFI_ERROR (Status)) {
    //
    // Maybe we need to find a PAD file that can hold this image
    //
    Status = FvCreateNewFileInsidePadFile (
              FvDevice,
              FfsFileBuffer,
              BufferSize,
              ActualFileSize,
              FileName,
              FileType,
              FileAttributes
              );

    return Status;
  }

  BufferPtr     = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;

  //
  // If we need a leading PAD File, create it first.
  //
  if (PadFileSize != 0) {
    Status = FvCreatePadFileInFreeSpace (
              FvDevice,
              FreeSpaceEntry,
              PadFileSize - sizeof (EFI_FFS_FILE_HEADER),
              &PadFileEntry
              );
    if (EFI_ERROR (Status)) {
      return Status;
    }
  }
  //
  // Maybe we create a pad file, so re-get the free space starting address
  // and length
  //
  BufferPtr     = (EFI_PHYSICAL_ADDRESS) (UINTN) FreeSpaceEntry->StartingAddress;

  //
  // File creation step 1: Allocate File Header,
  // Mark EFI_FILE_HEADER_CONSTRUCTION bit to TRUE,
  // Write Name, IntegrityCheck.Header, Type, Attributes, and Size
  //
  FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileBuffer;
  if (ActualFileSize > 0x00FFFFFF) {
    HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
  } else {
    HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
  }
  SetFileState (EFI_FILE_HEADER_CONSTRUCTION, FileHeader);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
  StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            StateOffset,
            &NumBytesWritten,
            &FileHeader->State
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // update header 2 cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FileHeader,
    HeaderSize
    );

  //
  // update Free Space Entry, now need to substract the file header length
  //
  FreeSpaceEntry->StartingAddress += HeaderSize;
  FreeSpaceEntry->Length -= HeaderSize;

  CopyGuid (&FileHeader->Name, FileName);
  FileHeader->Type = FileType;

  //
  // Convert FvFileAttribute to FfsFileAttributes
  //
  FvFileAttrib2FfsFileAttrib (FileAttributes, &TmpFileAttribute);

  FileHeader->Attributes = TmpFileAttribute;

  //
  // File size is including the FFS File Header.
  //
  if (ActualFileSize > 0x00FFFFFF) {
    ((EFI_FFS_FILE_HEADER2 *) FileHeader)->ExtendedSize = (UINT32) ActualFileSize;
    *(UINT32 *) FileHeader->Size &= 0xFF000000;
    FileHeader->Attributes |= FFS_ATTRIB_LARGE_FILE;
  } else {
    *(UINT32 *) FileHeader->Size &= 0xFF000000;
    *(UINT32 *) FileHeader->Size |= ActualFileSize;
  }

  SetHeaderChecksum (FileHeader);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);

  NumBytesWritten = HeaderSize;
  Status = FvcWrite (
            FvDevice,
            Offset,
            &NumBytesWritten,
            (UINT8 *) FileHeader
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // update header 2 cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FileHeader,
    HeaderSize
    );

  //
  // end of step 1
  //
  // File creation step 2:
  // MARK EFI_FILE_HEADER_VALID bit to TRUE,
  // Write IntegrityCheck.File, File Data
  //
  SetFileState (EFI_FILE_HEADER_VALID, FileHeader);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
  StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            StateOffset,
            &NumBytesWritten,
            &FileHeader->State
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // update header 2 cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FileHeader,
    HeaderSize
    );

  //
  // update Free Space Entry, now need to substract the file data length
  //
  FreeSpaceEntry->StartingAddress += (BufferSize - HeaderSize);
  FreeSpaceEntry->Length -= (BufferSize - HeaderSize);

  //
  // Calculate File Checksum
  //
  SetFileChecksum (FileHeader, ActualFileSize);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);

  NumBytesWritten = BufferSize;
  Status = FvcWrite (
            FvDevice,
            Offset,
            &NumBytesWritten,
            FfsFileBuffer
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // each time write block successfully, write also to cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FfsFileBuffer,
    NumBytesWritten
    );

  //
  // Step 3: Mark EFI_FILE_DATA_VALID to TRUE
  //
  SetFileState (EFI_FILE_DATA_VALID, FileHeader);

  Offset          = (UINTN) (BufferPtr - FvDevice->CachedFv);
  StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            StateOffset,
            &NumBytesWritten,
            &FileHeader->State
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // update header 2 cache
  //
  CopyMem (
    (UINT8 *) (UINTN) BufferPtr,
    FileHeader,
    HeaderSize
    );

  //
  // If successfully, insert an FfsFileEntry at the end of ffs file list
  //

  FfsFileEntry            = AllocateZeroPool (sizeof (FFS_FILE_LIST_ENTRY));
  ASSERT (FfsFileEntry   != NULL);
  FfsFileEntry->FfsHeader = (UINT8 *) (UINTN) BufferPtr;
  InsertTailList (&FvDevice->FfsFileListHeader, &FfsFileEntry->Link);

  //
  // Set cache file to this file
  //
  FvDevice->CurrentFfsFile = FfsFileEntry;

  return EFI_SUCCESS;
}

/**
  Update a File, so after successful update, there are 2 files existing
  in FV, one is marked for deleted, and another one is valid.

  @param FvDevice          Cached Firmware Volume.
  @param FfsFileBuffer     A buffer that holds an FFS file,(it contains
                           a File Header which is in init state).
  @param BufferSize        The size of FfsFileBuffer.
  @param ActualFileSize    The actual file length, it may not be multiples of 8.
  @param FileName          The FFS File Name.
  @param NewFileType       The FFS File Type.
  @param NewFileAttributes The Attributes of the FFS File to be created.

  @retval EFI_SUCCESS           FFS fle is updated into FV.
  @retval EFI_INVALID_PARAMETER File type is not valid.
  @retval EFI_DEVICE_ERROR      FV doesn't set writable attribute.
  @retval EFI_NOT_FOUND         FV has no enough space for the added file.
                                FFS with same file name is not found in FV.

**/
EFI_STATUS
FvUpdateFile (
  IN FV_DEVICE                *FvDevice,
  IN UINT8                    *FfsFileBuffer,
  IN UINTN                    BufferSize,
  IN UINTN                    ActualFileSize,
  IN EFI_GUID                 *FileName,
  IN EFI_FV_FILETYPE          NewFileType,
  IN EFI_FV_FILE_ATTRIBUTES   NewFileAttributes
  )
{
  EFI_STATUS                          Status;
  EFI_FIRMWARE_VOLUME2_PROTOCOL       *Fv;
  UINTN                               NumBytesWritten;
  EFI_FV_FILETYPE                     OldFileType;
  EFI_FV_FILE_ATTRIBUTES              OldFileAttributes;
  UINTN                               OldFileSize;
  EFI_FFS_FILE_HEADER                 *OldFileHeader;
  UINTN                               OldOffset;
  UINTN                               OldStateOffset;
  FFS_FILE_LIST_ENTRY                 *OldFfsFileEntry;
  UINTN                               Key;
  EFI_GUID                            FileNameGuid;

  Fv  = &FvDevice->Fv;

  //
  // Step 1, find old file,
  // Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE in the older header
  //

  //
  // Check if the file was read last time.
  //
  OldFileHeader   = NULL;
  OldFfsFileEntry = FvDevice->CurrentFfsFile;

  if (OldFfsFileEntry != NULL) {
    OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader;
  }

  if ((OldFfsFileEntry == NULL) || (!CompareGuid (&OldFileHeader->Name, FileName))) {
    Key = 0;
    do {
      OldFileType = 0;
      Status = Fv->GetNextFile (
                    Fv,
                    &Key,
                    &OldFileType,
                    &FileNameGuid,
                    &OldFileAttributes,
                    &OldFileSize
                    );
      if (EFI_ERROR (Status)) {
        return Status;
      }
    } while (!CompareGuid (&FileNameGuid, FileName));

    //
    // Get FfsFileEntry from the search key
    //
    OldFfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key;

    //
    // Double check file state before being ready to be removed
    //
    OldFileHeader = (EFI_FFS_FILE_HEADER *) OldFfsFileEntry->FfsHeader;
  } else {
    //
    // Mark the cache file to invalid
    //
    FvDevice->CurrentFfsFile = NULL;
  }
  //
  // Update File: Mark EFI_FILE_MARKED_FOR_UPDATE to TRUE
  //
  SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader);

  OldOffset       = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv);
  OldStateOffset  = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            OldStateOffset,
            &NumBytesWritten,
            &OldFileHeader->State
            );
  if (EFI_ERROR (Status)) {
    //
    // if failed, write the bit back in the cache, its XOR operation.
    //
    SetFileState (EFI_FILE_MARKED_FOR_UPDATE, OldFileHeader);

    return Status;
  }

  //
  // Step 2, Create New Files
  //
  Status = FvCreateNewFile (
            FvDevice,
            FfsFileBuffer,
            BufferSize,
            ActualFileSize,
            FileName,
            NewFileType,
            NewFileAttributes
            );
  if (EFI_ERROR (Status)) {
    return Status;
  }

  //
  // If successfully, remove this file entry,
  // although delete file may fail.
  //
  (OldFfsFileEntry->Link.BackLink)->ForwardLink = OldFfsFileEntry->Link.ForwardLink;
  (OldFfsFileEntry->Link.ForwardLink)->BackLink = OldFfsFileEntry->Link.BackLink;
  FreePool (OldFfsFileEntry);

  //
  // Step 3: Delete old files,
  // by marking EFI_FILE_DELETED to TRUE
  //
  SetFileState (EFI_FILE_DELETED, OldFileHeader);

  OldOffset       = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) OldFileHeader - FvDevice->CachedFv);
  OldStateOffset  = OldOffset + (UINT8 *) &OldFileHeader->State - (UINT8 *) OldFileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            OldStateOffset,
            &NumBytesWritten,
            &OldFileHeader->State
            );
  if (EFI_ERROR (Status)) {
    //
    // if failed, write the bit back in the cache, its XOR operation.
    //
    SetFileState (EFI_FILE_DELETED, OldFileHeader);

    return Status;
  }

  return EFI_SUCCESS;
}

/**
  Deleted a given file from FV device.

  @param FvDevice        Cached Firmware Volume.
  @param NameGuid        The FFS File Name.

  @retval EFI_SUCCESS    FFS file with the specified FFS name is removed.
  @retval EFI_NOT_FOUND  FFS file with the specified FFS name is not found.

**/
EFI_STATUS
FvDeleteFile (
  IN FV_DEVICE  *FvDevice,
  IN EFI_GUID   *NameGuid
  )
{
  EFI_STATUS                          Status;
  UINTN                               Key;
  EFI_GUID                            FileNameGuid;
  EFI_FV_FILETYPE                     FileType;
  EFI_FV_FILE_ATTRIBUTES              FileAttributes;
  UINTN                               FileSize;
  EFI_FFS_FILE_HEADER                 *FileHeader;
  FFS_FILE_LIST_ENTRY                 *FfsFileEntry;
  EFI_FFS_FILE_STATE                  FileState;
  EFI_FIRMWARE_VOLUME2_PROTOCOL        *Fv;
  UINTN                               Offset;
  UINTN                               StateOffset;
  UINTN                               NumBytesWritten;

  Fv  = &FvDevice->Fv;

  //
  // Check if the file was read last time.
  //
  FileHeader    = NULL;
  FfsFileEntry  = FvDevice->CurrentFfsFile;

  if (FfsFileEntry != NULL) {
    FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader;
  }

  if ((FfsFileEntry == NULL) || (!CompareGuid (&FileHeader->Name, NameGuid))) {
    //
    // Next search for the file using GetNextFile
    //
    Key = 0;
    do {
      FileType = 0;
      Status = Fv->GetNextFile (
                    Fv,
                    &Key,
                    &FileType,
                    &FileNameGuid,
                    &FileAttributes,
                    &FileSize
                    );
      if (EFI_ERROR (Status)) {
        return Status;
      }
    } while (!CompareGuid (&FileNameGuid, NameGuid));

    //
    // Get FfsFileEntry from the search key
    //
    FfsFileEntry = (FFS_FILE_LIST_ENTRY *) Key;

    //
    // Double check file state before being ready to be removed
    //
    FileHeader = (EFI_FFS_FILE_HEADER *) FfsFileEntry->FfsHeader;
  } else {
    //
    // Mark the cache file to NULL
    //
    FvDevice->CurrentFfsFile = NULL;
  }

  FileState = GetFileState (FvDevice->ErasePolarity, FileHeader);

  if (FileState == EFI_FILE_HEADER_INVALID) {
    return EFI_NOT_FOUND;
  }

  if (FileState == EFI_FILE_DELETED) {
    return EFI_NOT_FOUND;
  }
  //
  // Delete File: Mark EFI_FILE_DELETED to TRUE
  //
  SetFileState (EFI_FILE_DELETED, FileHeader);

  Offset          = (UINTN) ((EFI_PHYSICAL_ADDRESS) (UINTN) FileHeader - FvDevice->CachedFv);
  StateOffset     = Offset + (UINT8 *) &FileHeader->State - (UINT8 *) FileHeader;

  NumBytesWritten = sizeof (EFI_FFS_FILE_STATE);
  Status = FvcWrite (
            FvDevice,
            StateOffset,
            &NumBytesWritten,
            &FileHeader->State
            );
  if (EFI_ERROR (Status)) {
    //
    // if failed, write the bit back in the cache, its XOR operation.
    //
    SetFileState (EFI_FILE_DELETED, FileHeader);

    return Status;
  }
  //
  // If successfully, remove this file entry
  //
  FvDevice->CurrentFfsFile                    = NULL;

  (FfsFileEntry->Link.BackLink)->ForwardLink  = FfsFileEntry->Link.ForwardLink;
  (FfsFileEntry->Link.ForwardLink)->BackLink  = FfsFileEntry->Link.BackLink;
  FreePool (FfsFileEntry);

  return EFI_SUCCESS;
}

/**
  Writes one or more files to the firmware volume.

  @param  This                   Indicates the calling context.
  @param  NumberOfFiles          Number of files.
  @param  WritePolicy            WritePolicy indicates the level of reliability
                                 for the write in the event of a power failure or
                                 other system failure during the write operation.
  @param  FileData               FileData is an pointer to an array of
                                 EFI_FV_WRITE_DATA. Each element of array
                                 FileData represents a file to be written.

  @retval EFI_SUCCESS            Files successfully written to firmware volume
  @retval EFI_OUT_OF_RESOURCES   Not enough buffer to be allocated.
  @retval EFI_DEVICE_ERROR       Device error.
  @retval EFI_WRITE_PROTECTED    Write protected.
  @retval EFI_NOT_FOUND          Not found.
  @retval EFI_INVALID_PARAMETER  Invalid parameter.
  @retval EFI_UNSUPPORTED        This function not supported.

**/
EFI_STATUS
EFIAPI
FvWriteFile (
  IN CONST EFI_FIRMWARE_VOLUME2_PROTOCOL   *This,
  IN UINT32                         NumberOfFiles,
  IN EFI_FV_WRITE_POLICY            WritePolicy,
  IN EFI_FV_WRITE_FILE_DATA         *FileData
  )
{
  EFI_STATUS                          Status;
  UINTN                               Index1;
  UINTN                               Index2;
  UINT8                               *FileBuffer;
  UINTN                               BufferSize;
  UINTN                               ActualSize;
  UINT8                               ErasePolarity;
  FV_DEVICE                           *FvDevice;
  EFI_FV_FILETYPE                     FileType;
  EFI_FV_FILE_ATTRIBUTES              FileAttributes;
  UINTN                               Size;
  BOOLEAN                             CreateNewFile[MAX_FILES];
  UINTN                               NumDelete;
  EFI_FV_ATTRIBUTES                   FvAttributes;
  UINT32                              AuthenticationStatus;
  UINTN                               HeaderSize;

  if (NumberOfFiles > MAX_FILES) {
    return EFI_UNSUPPORTED;
  }

  Status = EFI_SUCCESS;

  SetMem (CreateNewFile, NumberOfFiles, TRUE);

  FvDevice  = FV_DEVICE_FROM_THIS (This);

  //
  // First check the volume attributes.
  //
  Status = This->GetVolumeAttributes (
                  This,
                  &FvAttributes
                  );
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Can we have write right?
  //
  if ((FvAttributes & EFI_FV2_WRITE_STATUS) == 0) {
    return EFI_WRITE_PROTECTED;
  }

  ErasePolarity = FvDevice->ErasePolarity;

  //
  // Loop for all files
  //
  NumDelete = 0;
  for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {

    if ((FileData[Index1].BufferSize + sizeof (EFI_FFS_FILE_HEADER) > 0x00FFFFFF) && !FvDevice->IsFfs3Fv) {
      //
      // Found a file needs a FFS3 formatted file to store it, but it is in a non-FFS3 formatted FV.
      //
      DEBUG ((EFI_D_ERROR, "FFS3 formatted file can't be written in a non-FFS3 formatted FV.\n"));
      return EFI_INVALID_PARAMETER;
    }

    if (FileData[Index1].BufferSize == 0) {
      //
      // Here we will delete this file
      //
      Status = This->ReadFile (
                      This,
                      FileData[Index1].NameGuid,
                      NULL,
                      &Size,
                      &FileType,
                      &FileAttributes,
                      &AuthenticationStatus
                      );
      if (!EFI_ERROR (Status)) {
        NumDelete++;
      } else {
        return Status;
      }
    }

    if (FileData[Index1].Type == EFI_FV_FILETYPE_FFS_PAD) {
      //
      // According to PI spec, on EFI_FV_FILETYPE_FFS_PAD:
      // "Standard firmware file system services will not return the handle of any pad files,
      // nor will they permit explicit creation of such files."
      //
      return EFI_INVALID_PARAMETER;
    }
  }

  if ((NumDelete != NumberOfFiles) && (NumDelete != 0)) {
    //
    // A delete was request with a multiple file write
    //
    return EFI_INVALID_PARAMETER;
  }

  if (NumDelete == NumberOfFiles) {
    for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
      //
      // Delete Files
      //
      Status = FvDeleteFile (FvDevice, FileData[Index1].NameGuid);
      if (EFI_ERROR (Status)) {
        return Status;
      }
    }

    return EFI_SUCCESS;
  }

  for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
    Status = This->ReadFile (
                    This,
                    FileData[Index1].NameGuid,
                    NULL,
                    &Size,
                    &FileType,
                    &FileAttributes,
                    &AuthenticationStatus
                    );
    if (!EFI_ERROR (Status)) {
      CreateNewFile[Index1] = FALSE;
    } else if (Status == EFI_NOT_FOUND) {
      CreateNewFile[Index1] = TRUE;
    } else {
      return Status;
    }
    //
    // Checking alignment
    //
    if ((FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT) != 0) {
      UINT8 FFSAlignmentValue;
      UINT8 FvAlignmentValue;

      FFSAlignmentValue = (UINT8) (FileData[Index1].FileAttributes & EFI_FV_FILE_ATTRIB_ALIGNMENT);
      FvAlignmentValue = (UINT8) (((UINT32) (FvAttributes & EFI_FV2_ALIGNMENT)) >> 16);

      if (FFSAlignmentValue > FvAlignmentValue) {
        return EFI_INVALID_PARAMETER;
      }
    }
  }

  if ((WritePolicy != EFI_FV_RELIABLE_WRITE) && (WritePolicy != EFI_FV_UNRELIABLE_WRITE)) {
    return EFI_INVALID_PARAMETER;
  }
  //
  // Checking the reliable write is supported by FV
  //

  if ((WritePolicy == EFI_FV_RELIABLE_WRITE) && (NumberOfFiles > 1)) {
    //
    // Only for multiple files, reliable write is meaningful
    //
    Status = FvCreateMultipleFiles (
              FvDevice,
              NumberOfFiles,
              FileData,
              CreateNewFile
              );

    return Status;
  }

  for (Index1 = 0; Index1 < NumberOfFiles; Index1++) {
    //
    // Making Buffersize QWORD boundary, and add file tail.
    //
    HeaderSize = sizeof (EFI_FFS_FILE_HEADER);
    ActualSize = FileData[Index1].BufferSize + HeaderSize;
    if (ActualSize > 0x00FFFFFF) {
      HeaderSize = sizeof (EFI_FFS_FILE_HEADER2);
      ActualSize = FileData[Index1].BufferSize + HeaderSize;
    }
    BufferSize  = ActualSize;

    while ((BufferSize & 0x07) != 0) {
      BufferSize++;
    }

    FileBuffer = AllocateZeroPool (BufferSize);
    if (FileBuffer == NULL) {
      return Status;
    }
    //
    // Copy File Data into FileBuffer
    //
    CopyMem (
      FileBuffer + HeaderSize,
      FileData[Index1].Buffer,
      FileData[Index1].BufferSize
      );

    if (ErasePolarity == 1) {
      //
      // Fill the file header and padding byte with Erase Byte
      //
      for (Index2 = 0; Index2 < HeaderSize; Index2++) {
        FileBuffer[Index2] = (UINT8)~FileBuffer[Index2];
      }

      for (Index2 = ActualSize; Index2 < BufferSize; Index2++) {
        FileBuffer[Index2] = (UINT8)~FileBuffer[Index2];
      }
    }

    if (CreateNewFile[Index1]) {
      Status = FvCreateNewFile (
                FvDevice,
                FileBuffer,
                BufferSize,
                ActualSize,
                FileData[Index1].NameGuid,
                FileData[Index1].Type,
                FileData[Index1].FileAttributes
                );
    } else {
      Status = FvUpdateFile (
                FvDevice,
                FileBuffer,
                BufferSize,
                ActualSize,
                FileData[Index1].NameGuid,
                FileData[Index1].Type,
                FileData[Index1].FileAttributes
                );
    }

    FreePool (FileBuffer);

    if (EFI_ERROR (Status)) {
      return Status;
    }
  }

  return EFI_SUCCESS;
}
